package com.weilive.core.code.writers;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;

import com.weilive.core.code.parsers.ProtoFileParser;
import com.weilive.core.code.parsers.ProtoFileParser.Field;
import com.weilive.core.code.parsers.ProtoFileParser.Message;
import com.weilive.core.code.parsers.ProtoFileParser.ProtoFile;
import com.weilive.core.utils.FileUtils;
import com.weilive.core.utils.LinuxUtils;
import com.weilive.core.utils.StringUtils;

public class ProtoToServerCode {
	private static String import_json_request = "import com.weilive.core.protocol.json.JsonRequest;";
	private static String import_json_response = "import com.weilive.core.protocol.json.JsonResponse;";
	private static String import_json_handler = "import com.weilive.core.protocol.json.JsonRequestHandler;";

	private static String import_proto_request = "import com.weilive.core.protocol.protobuf.ProtobufRequest;";
	private static String import_proto_response = "import com.weilive.core.protocol.protobuf.ProtobufResponse;";
	private static String import_proto_handler = "import com.weilive.core.protocol.protobuf.ProtobufRequestHandler;";

	private static final String write_file = "all_packets";
	private static final String java_class = "ProtocolPackets";

	public static void writeJsonProtocol(ProtoFile protoFile, String writeDir, String package_name) throws Exception {
		FileUtils.createDirs(writeDir);
		for (Message message : protoFile.messages) {
			PrintWriter writer = new PrintWriter(writeDir + message.name + ".java", "UTF-8");
			writer.println(String.format("package %s;", package_name));
			if (message.hasList) {
				writer.println(String.format(""));
				writer.println(String.format("import java.util.List;"));
			}
			writer.println(String.format(""));
			writer.println(String.format("public class %s {", message.name));
			writer.println(String.format(""));
			for (Field field : message.fields) {
				writer.println(String.format("    public %s %s;\t\t%s", field.type, field.name, field.comment));
			}
			writer.println(String.format(""));
			for (Field field : message.fields) {
				writer.println(String.format("    public %s get%s() {", field.type, StringUtils.firstUpcase(field.name)));
				writer.println(String.format("        return %s;", field.name));
				writer.println(String.format("    }"));
				writer.println(String.format(""));
				writer.println(String.format("    public void set%s(%s %s) {", StringUtils.firstUpcase(field.name), field.type, field.name));
				writer.println(String.format("        this.%s = %s;", field.name, field.name));
				writer.println(String.format("    }"));
				writer.println(String.format(""));
			}
			writer.println(String.format("}"));
			writer.close();
		}
	}

	public static void writeJsonHandler(ProtoFile protoFile, String writeDir, String package_name, String protocol_package) throws Exception {
		FileUtils.createDirs(writeDir);
		for (Message message : protoFile.messages) {
			if (message.name.startsWith("CSQ") == false && message.name.startsWith("CSF") == false && message.name.startsWith("SSQ") == false && message.name.startsWith("SSF") == false)
				continue;
			String responseType = message.name.startsWith("CSQ") ? message.name.replace("CSQ", "CSA") : message.name.startsWith("SSQ") ? message.name.replace("SSQ", "SSA") : null;

			String file = writeDir + message.name + "_JsonHandler.java";
			if (FileUtils.isFileExist(file))
				continue;
			System.out.println("Write file:" + file);

			PrintWriter writer = new PrintWriter(file, "UTF-8");
			writer.println(String.format("package %s;", package_name));
			writer.println(String.format(""));
			writer.println(String.format("import org.springframework.stereotype.Service;"));
			writer.println(String.format(""));
			writer.println(String.format(import_json_request));
			writer.println(String.format(import_json_handler));
			writer.println(String.format(import_json_response));
			writer.println(String.format("import %s.%s;", protocol_package, message.name));
			if (responseType != null) {
				writer.println(String.format("import %s.%s;", protocol_package, responseType));
			}
			writer.println(String.format(""));
			writer.println(String.format("@Service"));
			writer.println(String.format("public class %s_JsonHandler extends JsonRequestHandler {", message.name));
			writer.println(String.format(""));
			writer.println(String.format("    public Class<?> getRequestType() {"));
			writer.println(String.format("        return %s.class;", message.name));
			writer.println(String.format("    }"));
			writer.println(String.format(""));
			writer.println(String.format("    public Class<?> getResponseType() {"));
			if (responseType != null) {
				writer.println(String.format("        return %s.class;", responseType));
			} else {
				writer.println(String.format("        return null;"));
			}
			writer.println(String.format("    }"));
			writer.println(String.format(""));
			writer.println(String.format("    public JsonResponse handleRequest(JsonRequest request) throws Exception {"));
			writer.println(String.format("        // TODO"));
			writer.println(String.format("        %s params = (%s) request.getData();", message.name, message.name));
			if (responseType != null) {
				writer.println(String.format("        %s response = new %s();", responseType, responseType));
				writer.println(String.format("        return JsonResponse.create(response);"));
			} else {
				writer.println(String.format("        return null;"));
			}
			writer.println(String.format("    }"));
			writer.println(String.format(""));
			writer.println(String.format("}"));
			writer.close();
		}
	}

	public static void writeProtoHandler(ProtoFile protoFile, String writeDir, String package_name) throws Exception {
		FileUtils.createDirs(writeDir);
		for (Message message : protoFile.messages) {
			if (message.name.startsWith("CSQ") == false && message.name.startsWith("CSF") == false && message.name.startsWith("SSQ") == false && message.name.startsWith("SSF") == false)
				continue;
			String responseType = message.name.startsWith("CSQ") ? message.name.replace("CSQ", "CSA") : message.name.startsWith("SSQ") ? message.name.replace("SSQ", "SSA") : null;

			String file = writeDir + message.name + "_ProtobufHandler.java";
			if (FileUtils.isFileExist(file))
				continue;
			System.out.println("Write file:" + file);

			PrintWriter writer = new PrintWriter(file, "UTF-8");
			writer.println(String.format("package %s;", package_name));
			writer.println(String.format(""));
			writer.println(String.format("import org.springframework.stereotype.Service;"));
			writer.println(String.format(""));
			writer.println(String.format("import com.google.protobuf.MessageLite;"));
			writer.println(String.format(import_proto_request));
			writer.println(String.format(import_proto_handler));
			writer.println(String.format(import_proto_response));
			writer.println(String.format("import %s.%s.%s;", protoFile.java_package, protoFile.java_outer, message.name));
			if (responseType != null) {
				writer.println(String.format("import %s.%s.%s;", protoFile.java_package, protoFile.java_outer, responseType));
			}
			writer.println(String.format(""));
			writer.println(String.format("@Service"));
			writer.println(String.format("public class %s_ProtobufHandler extends ProtobufRequestHandler {", message.name));
			writer.println(String.format(""));
			writer.println(String.format("    public MessageLite getRequestType() {"));
			writer.println(String.format("        return %s.getDefaultInstance();", message.name));
			writer.println(String.format("    }"));
			writer.println(String.format(""));
			writer.println(String.format("    public MessageLite getResponseType() {"));
			if (responseType != null) {
				writer.println(String.format("        return %s.getDefaultInstance();", responseType));
			} else {
				writer.println(String.format("        return null;"));
			}
			writer.println(String.format("    }"));
			writer.println(String.format(""));
			writer.println(String.format("    public ProtobufResponse handleRequest(ProtobufRequest request) throws Exception {"));
			writer.println(String.format("        // TODO"));
			writer.println(String.format("        %s params = (%s)request.getData();", message.name, message.name));
			if (responseType != null) {
				writer.println(String.format("        %s.Builder response = %s.newBuilder();", responseType, responseType));
				writer.println(String.format("        return ProtobufResponse.create(response);"));
			} else {
				writer.println(String.format("        return null;"));
			}
			writer.println(String.format("    }"));
			writer.println(String.format(""));
			writer.println(String.format("}"));
			writer.close();
		}
	}

	public static void writeAtClassDir(String proto_file, String proto_handler_package, String json_protocol_package, String json_handler_package) throws Exception {

		String path = FileUtils.getClassDir();
		File file = new File(path + proto_file);
		ProtoFile protoFile = ProtoFileParser.parse(file);

		if (proto_handler_package != null) {
			String rootDir = FileUtils.getRootDir();
			String relative_path = FileUtils.getRelativeClassDir();
			String command = String.format("%sprotoc --java_out=%s %s", rootDir, relative_path, relative_path + proto_file);
			System.out.println(LinuxUtils.exec(command));

			writeProtoHandler(protoFile, path + proto_handler_package.replace(".", "/") + "/", proto_handler_package);
		}
		if (json_protocol_package != null && json_handler_package != null) {
			writeJsonProtocol(protoFile, path + json_protocol_package.replace(".", "/") + "/", json_protocol_package);
			writeJsonHandler(protoFile, path + json_handler_package.replace(".", "/") + "/", json_handler_package, json_protocol_package);
		}
	}

	private static void mergeProto(String protoDir, String protocol_proto_package) throws Exception {
		PrintWriter swriter = new PrintWriter(protoDir + write_file + ".proto", "UTF-8");
		// PrintWriter cwriter = new PrintWriter(protoDir + write_file +
		// "_client.proto", "UTF-8");

		swriter.println(String.format("option optimize_for = SPEED;"));
		swriter.println(String.format(""));
		swriter.println(String.format("option java_package = \"%s\"; ", protocol_proto_package));
		swriter.println(String.format("option java_outer_classname = \"%s\";", java_class));
		swriter.println(String.format(""));

		// cwriter.println(String.format("package %s;", client_proto_package));
		// cwriter.println(String.format(""));
		// cwriter.println(String.format("option optimize_for = LITE_RUNTIME; "));
		// cwriter.println(String.format(""));

		for (File file : new File(protoDir).listFiles()) {
			if (file.isDirectory() || file.getName().startsWith(write_file) || file.getName().endsWith(".proto") == false) {
				continue;
			}
			System.out.println("Read file:" + file.getAbsolutePath());
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String line;
			while ((line = reader.readLine()) != null) {
				if (line.trim().startsWith("option ") == false && line.trim().startsWith("import ") == false) {
					swriter.println(line);
					// cwriter.println(line);
				}
			}
			reader.close();
		}
		swriter.close();
		// cwriter.close();
	}

	public static void MakeProtocol(String protocol_proto_path, String protobuf_protocol_package, String protobuf_handler_package, String json_protocol_package, String json_handler_package) throws Exception {
		String path = FileUtils.getClassDir();
		String rootDir = FileUtils.getRootDir();
		String protoFilePath = path + protocol_proto_path.replace(".", "/") + "/";
		mergeProto(protoFilePath, protobuf_protocol_package);

		File file = new File(protoFilePath + write_file + ".proto");
		ProtoFile protoFile = ProtoFileParser.parse(file);

		if (json_protocol_package != null) {
			String jsonPath = path + json_protocol_package.replace(".", "/") + "/";
			writeJsonProtocol(protoFile, jsonPath, json_protocol_package);
		}

		if (json_handler_package != null) {
			String jsonPath = path + json_handler_package.replace(".", "/") + "/";
			writeJsonHandler(protoFile, jsonPath, json_handler_package, json_protocol_package);
		}
		if (protobuf_handler_package != null) {
			String relative_path = FileUtils.getRelativeClassDir();
			String jsonPath = relative_path + protocol_proto_path.replace(".", "/") + "/" + write_file + ".proto";
			String command = String.format("%sprotoc --java_out=%s %s", rootDir, relative_path, jsonPath);
			System.out.println(command);
			System.out.println(LinuxUtils.exec(command));

			String handlerPath = path + protobuf_handler_package.replace(".", "/") + "/";
			writeProtoHandler(protoFile, handlerPath, protobuf_handler_package);
		}
	}

}
